﻿/*
   Copyright 2012 University of Southampton

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

using System;
using System.Web;
using System.Drawing;
using System.IO;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using System.Drawing.Imaging;
using Microsoft.SharePoint;
using System.Linq;
using uk.ac.soton.ses.DAL;
using VDS.RDF;
using System.Security.Principal;
using System.Collections.Generic;
using VDS.RDF.Writing;

namespace uk.ac.soton.ses.layouts
{
    public class RDFHandler : IHttpHandler
    {
        /// <summary>
        /// You will need to configure this handler in the web.config file of your 
        /// web and register it with IIS before being able to use it. For more information
        /// see the following link: http://go.microsoft.com/?linkid=8101007
        /// </summary>
        #region IHttpHandler Members

        public bool IsReusable
        {
            // Return false in case your Managed Handler cannot be reused for another request.
            // Usually this would be false in case you have some state information preserved per request.
            get { return false; }
        }

        public void ProcessRequest(HttpContext context)
        {
            bool readfile = true;
            bool flattenparams = false;

            if (!readfile)
            {
                WriteRDFToHttpContext(context.Response.OutputStream, flattenparams);
            }
            else
            {
                using (StreamReader sr = new StreamReader(File.OpenRead(@"C:\inetpub\wwwroot\mdc\rdfserver\cached.rdf")))
                using (StreamWriter sw = new StreamWriter(context.Response.OutputStream))
                {
                    sw.Write(sr.ReadToEnd());
                }
            }

            context.Response.ContentType = "application/rdf+xml";
        }

        private void WriteRDFToHttpContext(Stream outputStream, bool flattenparams)
        {
            //We need a graph first
            IGraph rdfGraph = new Graph();

            //Set up some name spaces
            rdfGraph.NamespaceMap.AddNamespace("dcterms", UriFactory.Create("http://purl.org/dc/terms/"));
            rdfGraph.NamespaceMap.AddNamespace("ore", UriFactory.Create("http://www.openarchives.org/ore/terms/"));
            rdfGraph.NamespaceMap.AddNamespace("hdcparams", UriFactory.Create("https://hdc.soton.ac.uk/params/"));
            rdfGraph.NamespaceMap.AddNamespace("hdcdatasets", UriFactory.Create("https://hdc.soton.ac.uk/_layouts/HDC/ExperimentDetails.aspx?ID="));

            //Load the experiments

            HDCConfigurationOptions hdcconfig = HDCConfiguration.GetConfigurationOptions();
            using (MaterialsMetadataContext dbcontext = new MaterialsMetadataContext(hdcconfig.DatabaseOptions.EntityConnectionString))
            {
                ////Lookup current user
                //WindowsIdentity identity = context.User.Identity as WindowsIdentity;

                ////Get the user's experiments
                //IEnumerable<Experiment> usersExperiments = null;

                ////A WMI Query might be required which needs elevated priveleges
                //SPSecurity.RunWithElevatedPrivileges(delegate()
                //{
                //    usersExperiments = dbcontext.GetExperimentsForUser(identity.User, HDCExperimentAccessLevels.ReadOnly);
                //});

                //IEnumerable<Experiment> reloadedExperiments =
                //    usersExperiments.Join(
                //        dbcontext.Experiments.Include("ExperimentParameters").Include("ExperimentChildren").Include("ExperimentCollectionExperimentMaps"),
                //        spexp => spexp.ID,
                //        selexp => selexp.ID,
                //        (spexp, selexp) => selexp
                //    );

                IEnumerable<Experiment> reloadedExperiments =
                        dbcontext.Experiments.Include("ExperimentType").Include("ExperimentParameters").Include("ExperimentChildren").Include("ExperimentCollectionExperimentMaps");

                //Create the RDF
                foreach (Experiment experiment in reloadedExperiments.ToList())
                {
                    IUriNode expNode = rdfGraph.CreateUriNode("hdcdatasets:" + experiment.ID);

                    //Add ID
                    rdfGraph.Assert(new Triple(expNode, rdfGraph.CreateUriNode("dcterms:identifier"), experiment.ID.ToLiteral(rdfGraph)));
                    
                    //Add name
                    rdfGraph.Assert(new Triple(expNode, rdfGraph.CreateUriNode("dcterms:title"), rdfGraph.CreateLiteralNode(experiment.Name)));

                    //Add description (if present)
                    if (!string.IsNullOrEmpty(experiment.Description))
                    {
                        rdfGraph.Assert(new Triple(expNode, rdfGraph.CreateUriNode("dcterms:description"), rdfGraph.CreateLiteralNode(experiment.Description)));
                    }

                    //Add experiment type
                    rdfGraph.Assert(new Triple(expNode, rdfGraph.CreateUriNode("dcterms:type"), rdfGraph.CreateLiteralNode(experiment.ExperimentType.Name)));

                    //Add date
                    //ISO 8601/W3C format is YYYY-MM-DDThh:mm:ssTZD [http://www.w3.org/TR/NOTE-datetime]
                    rdfGraph.Assert(new Triple(expNode, rdfGraph.CreateUriNode("dcterms:date"), rdfGraph.CreateLiteralNode(experiment.Date.ToString("yyyy-MM-ddTHH:mm:ssZ"))));

                    if (flattenparams)
                    {
                        foreach (ExperimentParameter param in experiment.ExperimentParameters)
                        {
                            if (!string.IsNullOrEmpty(param.Name))
                            {
                                //Remove unsupported characters
                                string paramName = param.Name;
                                if (!char.IsLetter(paramName[0]))
                                {
                                    //If the first letter is not a letter, we can't continue as we can't support e.g. a number. Skip this param
                                    continue;
                                }

                                paramName = paramName.Replace(':', '_').Replace(' ', '_').Replace('/', '_');

                                //Try to help out by url encoding the remaining string
                                paramName = HttpUtility.UrlEncode(paramName);

                                rdfGraph.Assert(new Triple(expNode, rdfGraph.CreateUriNode("hdcparams:" + paramName), rdfGraph.CreateLiteralNode(param.Value??"")));
                            }
                        }
                    }
                    else
                    {
                        foreach (ExperimentParameter param in dbcontext.GetExperimentTopLevelParameters(experiment.ID, false))
                        {
                            this.AddParamNode(dbcontext, rdfGraph, expNode, param);
                        }
                    }

                    //Add experiment children
                    //experiment.ExperimentChildren.Load();
                    foreach (ExperimentLink link in experiment.ExperimentChildren)
                    {
                        rdfGraph.Assert(new Triple(expNode, rdfGraph.CreateUriNode("dcterms:references"), rdfGraph.CreateUriNode("hdcdatasets:" + link.LinkedExperimentID)));
                    }

                    foreach (ExperimentLink link in experiment.ExperimentParents)
                    {
                        rdfGraph.Assert(new Triple(expNode, rdfGraph.CreateUriNode("dcterms:isReferencedBy"), rdfGraph.CreateUriNode("hdcdatasets:" + link.ParentExperimentID)));
                    }

                    //Add experiments in collections
                    //experiment.ExperimentCollectionExperimentMaps.Load();
                    foreach (ExperimentCollectionExperimentMap map in experiment.ExperimentCollectionExperimentMaps)
                    {
                        if (!map.ExperimentCollectionReference.IsLoaded)
                        {
                            map.ExperimentCollectionReference.Load();
                        }
                    }

                    foreach (ExperimentCollection coll in experiment.ExperimentCollectionExperimentMaps.Select(map=>map.ExperimentCollection))
                    {
                        foreach (Experiment exp in coll.ExperimentCollectionExperimentMaps.Select(map => map.Experiment))
                        {
                            if (exp.ID != experiment.ID)
                            {
                                rdfGraph.Assert(new Triple(expNode, rdfGraph.CreateUriNode("dcterms:references"), rdfGraph.CreateUriNode("hdcdatasets:" + exp.ID)));
                            }
                        }
                    }
                }
            }

            //Write the RDF to the output stream
            RdfXmlWriter xmlwriter = new RdfXmlWriter(0, false);
            xmlwriter.PrettyPrintMode = true;
            using (TextWriter tw = new StreamWriter(outputStream))
            {
                xmlwriter.Save(rdfGraph, tw);
            }
        }

        private void AddParamNode(MaterialsMetadataContext dbcontext, IGraph rdfGraph, INode subject, ExperimentParameter param)
        {
            if (!string.IsNullOrEmpty(param.Name))
            {
                //Remove unsupported characters
                string paramName = param.Name;
                if (!char.IsLetter(paramName[0]))
                {
                    //If the first letter is not a letter, we can't continue as we can't support e.g. a number. Skip this param
                    return;
                }

                paramName = paramName.Replace(':', '_').Replace(' ', '_').Replace('/', '_');

                //Try to help out by url encoding the remaining string
                paramName = HttpUtility.UrlEncode(paramName);

                //Create a new UriNode for the parameter name predicate
                IUriNode newPredicate = rdfGraph.CreateUriNode("hdcparams:" + paramName);

                //Join the predicate to the value if it exists
                if (!string.IsNullOrEmpty(param.Value))
                {
                    ILiteralNode newLiteralNode = rdfGraph.CreateLiteralNode(param.Value ?? "");
                    rdfGraph.Assert(new Triple(subject, newPredicate, newLiteralNode));
                }

                List<ExperimentParameter> children = dbcontext.GetExperimentChildParameters(param.ID).ToList();
                if (children.Count() > 0)
                {
                    //Create a new blank node to hold the new nodes
                    IBlankNode newBlankNode = rdfGraph.CreateBlankNode();

                    //Join the new blank node to the subject
                    rdfGraph.Assert(new Triple(subject, newPredicate, newBlankNode));

                    foreach (ExperimentParameter childparam in children)
                    {
                        //Join the results of the recursion to the blank node
                        AddParamNode(dbcontext, rdfGraph, newBlankNode, childparam);
                    }
                }
            }
        }


        #endregion
    }
}
